﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DeepHash.Document;

namespace DeepHash
{
    public class DirectoryWalker
    {
        public class DirectoryWalkInfo
        {
            public enum EventType
            {
                EnterDirectory,
                BeginHash,
                EndHash,
                ExitDirectory, 
                Error
            }

            public DirectoryWalkInfo()
            {
            }

            public DirectoryWalkInfo(EventType type, string path, byte[] hash, Exception error)
            {
                _type  = type;
                _path  = path;
                _hash  = hash;
                _error = error;
            }

            private EventType _type;
            public EventType Type
            {
                get { return _type; }
                internal set { _type = value; }
            }

            private string _path = string.Empty;
            public string Path
            {
                get { return _path; }
                internal set { _path = value; }
            }

            private byte[] _hash;
            public byte[] Hash
            {
                get { return _hash; }
                internal set { _hash = value; }
            }

            private Exception _error = null;
            public Exception Error
            {
                get { return _error; }
                internal set { _error = value; }
            }


        }

        public delegate bool DirectoryWalkNotification (DirectoryWalkInfo info);
        public Document.VerificationDocument WalkDirectory(string path, IHashingAlgorithem algorithem, bool recursive, 
            DirectoryWalker.DirectoryWalkNotification notifier)
        {
            VerificationDocument doc = new VerificationDocument();
            
            // Record which algorithem we are using 
            doc.HashAlgorithem = "MD5"; // Utility.GetAlgorithem(algorithem.Name);
            
            // Check for valid directory path
            if (!Directory.Exists ( path))
            {
                throw new InvalidOperationException ("Path does not exist.");
            }

            DirectoryInfo dir = new DirectoryInfo(path);

            WalkDirectory (dir,doc, algorithem, recursive, notifier);
            return doc;
        
        }

        private bool SendCallback(DirectoryWalkNotification notifier, DirectoryWalkInfo info)
        {
            if (notifier != null)
            {
                return notifier.Invoke(info);
            }
            return true;
        }

        private bool WalkDirectory(DirectoryInfo dir, FolderVerificationRecord folder, IHashingAlgorithem algorithem, 
            bool recursive, DirectoryWalker.DirectoryWalkNotification notifier)
        {
            SendCallback(notifier, new DirectoryWalkInfo(DirectoryWalkInfo.EventType.EnterDirectory,
                dir.FullName, null, null));

            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                AnalyzeFile(file, folder, algorithem, notifier);    
            }

            if (recursive)
            {
                DirectoryInfo[] dirs = dir.GetDirectories();
                foreach (DirectoryInfo childFolder in dirs)
                {
                    FolderVerificationRecord folderRecord = new FolderVerificationRecord();
                    folderRecord.Name = childFolder.Name;
                    WalkDirectory(childFolder, folderRecord, algorithem, recursive, notifier);
                    folder.Folders.Add(folderRecord);
                }
            }

            SendCallback(notifier, new DirectoryWalkInfo(DirectoryWalkInfo.EventType.ExitDirectory,
                 dir.FullName, null, null));

            return true;
        }

        private bool AnalyzeFile(FileInfo file, FolderVerificationRecord folder, IHashingAlgorithem algorithem,
            DirectoryWalker.DirectoryWalkNotification notifier)
        {
            SendCallback(notifier, new DirectoryWalkInfo(DirectoryWalkInfo.EventType.BeginHash,
                file.FullName, null, null));

            FileVerificationRecord fileRecord = new FileVerificationRecord();
            fileRecord.Name = file.Name;
            FileStream fs = null;
            try
            {
                fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read);
                fileRecord.Hash = algorithem.GetHashValue(fs);
                folder.Files.Add(fileRecord);
            }
            catch
            {

            }
            finally
            {
                fs.Close();
            }
            SendCallback(notifier, new DirectoryWalkInfo(DirectoryWalkInfo.EventType.EndHash,
                 file.FullName, fileRecord.Hash, null));

            return true;
        }
    }
}